use anyhow::{anyhow, Result};
use auto_launch::{AutoLaunch, AutoLaunchBuilder};
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use std::env::current_exe;
use std::sync::Arc;

pub struct Osys {
    auto_launch: Arc<Mutex<Option<AutoLaunch>>>,
}

impl Osys {
    pub fn global() -> &'static Osys {
        static SYSOPT: OnceCell<Osys> = OnceCell::new();

        SYSOPT.get_or_init(|| Osys {
            auto_launch: Arc::new(Mutex::new(None)),
        })
    }

    pub fn init_launch(&self) -> Result<()> {
        let app_exe = current_exe()?;
        let app_name = app_exe
            .file_stem()
            .and_then(|f| f.to_str())
            .ok_or(anyhow!("failed to get file stem"))?;

        let app_path = app_exe
            .as_os_str()
            .to_str()
            .ok_or(anyhow!("failed to get app_path"))?
            .to_string();

        #[cfg(target_os = "windows")]
        let app_path = format!("\"{app_path}\"");

        #[cfg(target_os = "macos")]
        let app_path = (|| -> Option<String> {
            let path = std::path::PathBuf::from(&app_path);
            let path = path.parent()?.parent()?.parent()?;
            let extension = path.extension()?.to_str()?;
            match extension == "app" {
                true => Some(path.as_os_str().to_str()?.to_string()),
                false => None,
            }
        })()
        .unwrap_or(app_path);

        #[cfg(target_os = "linux")]
        let app_path = {
            use crate::core::handle::Handle;
            use tauri::Manager;

            let handle = Handle::global();
            match handle.app_handle.lock().as_ref() {
                Some(app_handle) => {
                    let appimage = app_handle.env().appimage;
                    appimage
                        .and_then(|p| p.to_str().map(|s| s.to_string()))
                        .unwrap_or(app_path)
                }
                None => app_path,
            }
        };

        let auto = AutoLaunchBuilder::new()
            .set_app_name(app_name)
            .set_app_path(&app_path)
            .build()?;
        *self.auto_launch.lock() = Some(auto);
        Ok(())
    }

    pub fn update_launch(&self, enable: bool) -> Result<()> {
        let auto_launch = self.auto_launch.lock();
        if auto_launch.is_none() {
            self.init_launch()?;
        }

        // 如果程序启动的时候调用disable，而原本注册表没有自动启动的话 就会在disable那边传播错误
        // 所以要忽略disable的错误
        match enable {
            true => auto_launch.as_ref().unwrap().enable()?,
            false => match auto_launch.as_ref().unwrap().disable() {
                _ => {}
            },
        };

        Ok(())
    }
}
